NULL equ 0x00
SETCHAR equ 0x07
VIDEOMEM equ 0xb800
STRINGLEN equ 0xffff

section head align=16 vstart=0
    Size dd ProgramEnd                      ;4B 0x00，整个程序的长度，包含头部分
    SegmentAddr:
    CodeSeg dd section.code.start           ;4B 0x04, nasm 提供的 '.' 语法，表示 code 段地址
    DataSeg dd section.data.start           ;4B 0x08
    StackSeg dd section.stack.start         ;4B 0x0C
    SegmentNum:
    SegNum db (SegmentNum-SegmentAddr)/4    ;1B 0x10，段的数量，由于每个段的地址固定占 4B，所以除以 4 就能知道具体有多少个段了
    Entry dw CodeStart                      ;2B 0x11，偏移地址
          dd section.code.start             ;4B 0x13，段地址

section code align=16 vstart=0
;声明程序入口点
CodeStart:
    mov ax, [DataSeg]   ;初始化 ds 和 si，指向字符串的开头
    mov ds, ax
    mov ax, [StackSeg]
    mov ss, ax
    mov sp, StackEnd
    xor si, si
    ;call ClearScreen
    call PrintLines    ;调用打印方法
    jmp $

PrintLines:
    push ax
    push bx
    push cx
    push dx

    mov cx, HellloEnd-Hello
    xor si, si
    mov bl, 0x07
    .putc:
    mov al, [si]
    inc si
    mov ah, 0x0e
    int 0x10
    loop .putc

    pop dx
    pop cx
    pop bx
    pop ax
    ret

;设置光标的函数
;输入参数：di 光标所在的位置，从 0 开始到 (80 * 25 - 1)
;
SetCursor:
    ;在操作前先将要使用的寄存器压栈
    push dx         
    push bx
    push ax

    ;计算出光标的位置，由于 di 存储是字节数，而字节数又是字符数的 2 倍，所以除以 2 就是字符数
    mov ax, di      ;存储被除数的低 16 位
    mov dx, 0       ;存储被除数的高 16 位
    mov bx, 2       ;存储除数
    div bx          ;进行 16 位的除法，除法的结果 AX 保存商，DX 保存余数

    ;设置光标的位置
    ;当 0x3d4 端口为 0x0e 时，0x3d5 端口保存的是光标地址的高 8 位
    ;当 0x3d4 端口为 0x0f 时，0x3d5 端口保存的是光标地址的低 8 位
    mov bx, ax      ;暂存光标的位置
    mov dx, 0x3d4   ;存储控制端口
    mov al, 0x04    ;存储控制指令
    out dx, al      ;向控制端口发送控制指令，表示 0x3d5 端口将要写入的是光标地址的高 8 位
    mov dx, 0x3d5   ;存储数据端口
    mov al, bh      ;存储光标地址的高 8 位
    out dx, al      ;将光标地址的高 8 位，写入到数据端口

    mov dx, 0x3d4    ;存储控制端口
    mov al, 0x0f    ;存储控制指令
    out dx, al      ;向控制端口发送控制指令，表示 0x3d5 端口将要写入的是光标地址的低 8 位
    mov dx, 0x3d5   ;存储数据端口
    mov al, bl      ;存储光标地址的低 8 位
    out dx, al      ;将光标地址的低 8 位，写入到数据端口

    pop ax
    pop bx
    pop dx
    ret

section data align=16 vstart=0
    Hello:      db 'Hello,I com from program on sector 1,loaded by bootloader!'
                db 0x0d, 0x0a
                db 'Haha, This is a new line!'
                db 0x0a
                db 'Just 0a'
                db 0x0d
                db 'Just od'
                db 0x0d, 0x0a
    HellloEnd:  db 0x00
section stack align=16 vstart=0
    times 128 db 0
    StackEnd:
section end align=16
    ProgramEnd: ;这个标号主要作用就是计算整个程序的长度